package com.fiill.audioplayer;

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.AudioAttributes;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;

import com.fiill.audioplayernativelib.WaveParseLib;

public class AudioTrackManager {
    private static AudioTrackManager mInstance;
    private static final String TAG = "BAHAL_ATRACK";
    private AudioTrack mAudioTrack;
    private int mBufferSize;
    private PlayThread mPlayThread;
    private boolean isPlaying = false;
    private static final int RATE = 48000;
    private static final int PERIOD_SIZE = 768;
    private static final int PERIOD_COUNT = 4;

    int bit_format;
    int channel_mask ;
    int channel_count;
    int offset;
    int dataSize;
    FileInputStream fileInputStream;


    private AudioTrackManager() {
        //
    }

    public static AudioTrackManager getInstance() {
        if (mInstance == null) {
            synchronized (AudioTrackManager.class) {
                if (mInstance == null) {
                    mInstance = new AudioTrackManager();
                }
            }
        }
        return mInstance;
    }

    public void adjustArgumentsFromWavHeader(String path){
        WaveParseLib waveParseLib = new WaveParseLib(path);
        offset = waveParseLib.getOffset();
        dataSize = waveParseLib.getDataSize();
        if(0 == bit_format) bit_format = Utils.getBitFormatFromBitWidth(waveParseLib.getBitFormat());
        if(0 == channel_mask) {
            channel_count = waveParseLib.getChannels();
            channel_mask = Utils.channelMaskFromChannelCount(channel_count);
        }
    }

    public void adjustArgumentsForPcm(String path){
        File file = new File(path);
        dataSize = (int)file.length();
        if(0 == bit_format) bit_format = AudioFormat.ENCODING_PCM_16BIT;
        if(0 == channel_mask || 0 == channel_count) {
            channel_count = 2;
            channel_mask = AudioFormat.CHANNEL_OUT_STEREO;
        }
    }

    public void startPlay(String path, String audio_context_str, String channel_mask_str, String bit_format_format_str) {
        bit_format = Utils.getBitFormatFromFormatStr(bit_format_format_str);
        channel_mask = Utils.channelMaskFromOutChannelMaskString(channel_mask_str);
        channel_count = AudioFormat.channelCountFromOutChannelMask(channel_mask);
        int audio_context = CarAudioContext.getAudioContextFromAudioContextString(audio_context_str);
        if(Utils.hasFileExtension(path, "wav")) {
            adjustArgumentsFromWavHeader(path);
        } else if(Utils.hasFileExtension(path, "pcm")) {
            adjustArgumentsForPcm(path);
        }

        int bit_width = Utils.getBitWidthFromFormat(bit_format);
        //frame_size * frame_count * channels * bit_width
        mBufferSize = PERIOD_SIZE * PERIOD_COUNT * channel_count * bit_width;
        if(0 == channel_mask || 0 == channel_count || 0 == mBufferSize || null == audio_context_str) {
            Log.d(TAG, "Unsupported media:" + path + " channel_mask:" + channel_mask + " audio_conext:" + audio_context_str);
            return;
        }
        Log.d(TAG, "Play " + path + " ch:" + channel_count +
                  " format:" + bit_format + " audio_conext:" + audio_context_str + " buffer_size:" + mBufferSize);
        mAudioTrack = new AudioTrack.Builder()
        .setAudioAttributes(new AudioAttributes.Builder()
                .setUsage(CarAudioContext.getAudioUsageFromAudioContext(audio_context))
                .setContentType(CarAudioContext.getDefaultContentTypeFromAudioContext(audio_context))
                .build())
        .setAudioFormat(new AudioFormat.Builder()
                .setEncoding(bit_format)
                .setSampleRate(RATE)
                .setChannelMask(channel_mask)
                .build())
        .setTransferMode(AudioTrack.MODE_STREAM)
        .setBufferSizeInBytes(mBufferSize)
        //.setSessionId(0)
        .build();

        if (isPlaying) {
            Log.e(TAG, "Already playing...");
        } else {
            isPlaying = true;
            mAudioTrack.play();
            mPlayThread = new PlayThread(path, offset, dataSize);
            mPlayThread.start();
        }
    }

    public void stopPlay() {
        if (!isPlaying) {
            Log.e(TAG, "Not ready yet");
        } else {
            isPlaying = false;
            Log.i(TAG, "Stop playing.");
            mAudioTrack.stop();
            if(null != fileInputStream) {
                try {
                    fileInputStream.close();
                } catch (Exception e) {
                }
                fileInputStream = null;
            }
        }
    }

    class PlayThread extends Thread {
        byte[] write_data = new byte[mBufferSize];
        int offset = 0;
        final String mPath;
        final int mDataSize;
        final int mHeaderLen;

        PlayThread(String path, int headerLen, int dataSize) {
            mPath = path;
            mDataSize = dataSize;
            mHeaderLen = headerLen;
        }
        @Override
        public void run() {
            // TODO Auto-generated method stub
            super.run();
            offset = mHeaderLen; //skip header length
            while(isPlaying){
                if((offset - mHeaderLen) >= mDataSize) {
                    stopPlay();
                    break;
                }
                try {
                    File file = new File(mPath);
                    fileInputStream = new FileInputStream(file);
                    fileInputStream.skip((long)offset);
                    fileInputStream.read(write_data, 0, mBufferSize);
                    offset += mBufferSize;
                } catch (Exception e) {
                    // TODO: handle exception
                    //TODO stop when EOF
                }
                mAudioTrack.write(write_data, 0, mBufferSize);
            }
        }
    }
}
